package controllers;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import repository.ItemRepository;
import repository.PurchaseRepository;
import repository.UserRepository;
import repository.impl.CustomerReportItem;
import validator.ForgotPasswordValidator;
import validator.ReportValidator;
import validator.UserFormValidator;

import command.ForgotPasswordForm;
import command.ReportForm;
import command.UserForm;

import domain.Purchase;
import domain.User;
import domain.exceptions.BigRangeException;
import domain.exceptions.DuplicateException;

@Controller
public class UserController{

	private UserRepository userRepository;
	private ItemRepository itemRepository;
	private PurchaseRepository purchaseRepository;
	private UserFormValidator validator;
	private ReportValidator reportValidator;
	private ForgotPasswordValidator forgotPasswordValidator;
	
	@Autowired
	public UserController(UserRepository userRepository, ItemRepository itemRepository,
			PurchaseRepository purchaseRepository,  
			UserFormValidator validator, ReportValidator reportValidator,
			ForgotPasswordValidator forgotPasswordValidator){
		this.userRepository=userRepository;
		this.itemRepository=itemRepository;
		this.purchaseRepository=purchaseRepository;
		this.validator=validator;
		this.reportValidator = reportValidator;
		this.forgotPasswordValidator = forgotPasswordValidator;
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView AbmUsers(){
		ModelAndView mav = new ModelAndView();
		mav.addObject("userForm", new UserForm());
		mav.addObject("title", "NEW USER");
		mav.addObject("action", "../user/AbmUsers");
		mav.setViewName("user/AbmUsers");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView AbmUsers( HttpSession session, UserForm userForm, Errors errors){
		ModelAndView mav = new ModelAndView();
		validator.validate(userForm, errors);
		if (errors.hasErrors()) {
			return mav;
		}
		try {
			User usuario=new User(userForm.getUserName(),userForm.getPassword(),userForm.getName(),
				userForm.getLastName(),userForm.getEmail(),userForm.getQuestion(),userForm.getAnswer(),
				userForm.getAddress(),userForm.getTelephone(),userForm.getCelular(),userForm.getBirthday(),
				userForm.isMale(), false);
			userRepository.newUser(usuario);
			session.setAttribute("user", usuario);
		} catch (DuplicateException e) {
			errors.rejectValue("userName", "dupUsername");
		}
		if (errors.hasErrors()) {
			mav.addObject("title", "NEW USER");
			mav.addObject("action", "../user/AbmUsers");
			return mav;
		}
		mav.setViewName("redirect:/bin/general/index");
		return mav;		
	}	

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView ModifyUser(HttpSession session){
		ModelAndView mav = new ModelAndView();
		User user = (User)session.getAttribute("user");
		
		UserForm userForm = new UserForm(user);
		
		mav.addObject("userForm", userForm);
		mav.addObject("title", "MODIFY USER");
		mav.addObject("action", "../user/ModifyUser");
		mav.addObject("username", userForm.getUserName());
		mav.setViewName("user/AbmUsers");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView ModifyUser(HttpSession session, UserForm userForm, Errors errors) throws ParseException{
		ModelAndView mav = new ModelAndView();
		validator.validate(userForm, errors);
		if (errors.hasErrors()) {
			mav.addObject("userForm", userForm);
			mav.addObject("title", "MODIFY USER");
			mav.addObject("action", "../user/ModifyUser");
			mav.addObject("username", userForm.getUserName());
			mav.setViewName("user/AbmUsers");
			return mav;
		}
		
		User user = (User)session.getAttribute("user");
		User userMod = userRepository.getUser(user.getUserName(), user.getEmail());
		userForm.update(userMod);
		
		session.setAttribute("user", userMod);
		return viewProfile(userMod, session);
	}
	
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView viewProfile(@RequestParam("userid") User user, HttpSession session) throws ParseException {
		ModelAndView mav = new ModelAndView();
		
		User userL = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		
		if(user==null){
			mav.addObject("errorTitle", "Error");
			mav.addObject("errorMsg", "Error de usuario");
			mav.setViewName("general/error");
			return mav;
		}
		mav.addObject("items",itemRepository.getItemsByUser(user, null, 0));
		mav.addObject("comments", purchaseRepository.getComment(user));
		mav.addObject("positive", user.getPositive());
		mav.addObject("neutral", user.getNeutral());
		mav.addObject("negative", user.getNegative());
		mav.addObject("userD", user);
		mav.addObject("userL", userL);
		
		if(userL!=null && userL.isAdmin()){
			List<String> debts = new ArrayList<String>();
			List<Purchase> sales = purchaseRepository.getSoldItems(user);
			List<Purchase> pendingDebtSales = new ArrayList<Purchase>();
			for (Purchase sale : sales) {
				if (sale.isDebtPending()) {
					pendingDebtSales.add(sale);
					double debt = sale.calcCommission();
					NumberFormat twoDForm =  NumberFormat.getInstance(Locale.getDefault());
					twoDForm.setMinimumFractionDigits(2);
					twoDForm.setMaximumFractionDigits(2);
					String num = twoDForm.format(debt);
					debts.add(num);
				}
			}

			sales.clear();
			sales = null; // libera la lista
			mav.addObject("purchases", pendingDebtSales);
			mav.addObject("debts", debts);
			mav.addObject("hasDebts", true);
		}
		mav.setViewName("user/viewProfile");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView listPurchaseDate(){
		ModelAndView mav = new ModelAndView();
		mav.addObject("reportForm", new ReportForm());
		mav.addObject("action", "../user/listPurchaseDate");
		mav.setViewName("user/listPurchaseDate");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView listPurchaseDate(ReportForm reportForm, Errors errors){
		reportValidator.validate(reportForm, errors);
		ModelAndView mav = new ModelAndView();
		if(errors.hasErrors()){
			mav.setViewName("user/listPurchaseDate");
			mav.addObject("reportForm", reportForm);
			return mav;
		}
		try {
			Map<Date, Long> purchases = purchaseRepository.getPurchases(reportForm.getBefore(), reportForm.getAfter());
			mav.addObject("purchases", purchases);
		}catch(BigRangeException e){
			errors.rejectValue("before", "bigrange");
			return null;
		}
		mav.setViewName("user/listPurchaseDate");
		return mav;		
	}	

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView listPurchaseCategory(){
		ModelAndView mav = new ModelAndView();
		Map<String, Long> purchases = purchaseRepository.getPurchasesByCategory();
		mav.addObject("purchases", purchases);
		mav.setViewName("user/listPurchaseCategory");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView listMaxBuyAndSell(){
		ModelAndView mav = new ModelAndView();
		Set<CustomerReportItem> buy = purchaseRepository.getMaxBuy();
		Set<CustomerReportItem> sell = purchaseRepository.getMaxSell();
		mav.addObject("buy", buy);
		mav.addObject("sell", sell);
		mav.setViewName("user/listMaxBuyAndSell");
		return mav;
	}
	@RequestMapping(method = RequestMethod.POST)
	protected ModelAndView operationHistoryPost(@RequestParam("opcode") int opcode, HttpSession session) {
		return operationHistory(opcode, session);
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView operationHistory(@RequestParam("opcode") int opcode, HttpSession session) {
		User user = null;
		List<Purchase> history = null;
		
		if(session.getAttribute("user") instanceof User)
			user = (User)session.getAttribute("user");
		
		ModelAndView mav = new ModelAndView();
		
		if (opcode == 0) {
			history = purchaseRepository.getPurchasedItems(user);
		} else if(opcode == 1) {
			history = purchaseRepository.getSoldItems(user);
		}
		
		if (history != null) {
			mav.addObject("history", history);
		}
		
		mav.addObject("opcode", opcode);
		mav.setViewName("/user/purchaseHistory");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView registeredUsers(HttpSession session) {
		List<User> rusers;
		User user = null;
		
		if(session.getAttribute("user") instanceof User) {
			user = (User)session.getAttribute("user");
		}
		ModelAndView mav = new ModelAndView();
		rusers = userRepository.getRegisteredUsers();
		if (rusers != null) {
			mav.addObject("users", rusers);
		}
		mav.addObject("name", user.getName());
		mav.setViewName("/user/ListUsers");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView forgotPassword() {
		ModelAndView mav = new ModelAndView();
		mav.addObject("forgotPasswordForm", new ForgotPasswordForm());
		mav.setViewName("user/forgotPassword");		
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView forgotPassword(ForgotPasswordForm forgotPasswordForm, Errors errors){
		forgotPasswordValidator.validate(forgotPasswordForm, errors);
		if(errors.hasErrors())
			return null;

		ModelAndView mav = new ModelAndView();
		User user = userRepository.getUser(forgotPasswordForm.getUserName(), forgotPasswordForm.getEmail());
		if (user==null){
			mav.addObject("notexist","The values don't match with any user");
			mav.setViewName("user/forgotPassword");
			return mav;
		}
		mav.addObject("lostuser", user);
		mav.setViewName("user/forgotPassword2");
		return mav;		
	}	
	
	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView forgotPassword2(@RequestParam("userId") User user,@RequestParam("answer") String answer){
		ModelAndView mav = new ModelAndView();
		mav.addObject("verifyAnswer",user.validateAnswer(answer));
		mav.addObject("answer",answer);
		mav.addObject("lostuser",user);
		mav.setViewName("user/forgotPassword2");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView updateBlockedStatus(@RequestParam("userId") User user, HttpSession session) {
		if(user!=null){
			user.updateBlockedStatus();
		}
		return registeredUsers(session);
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView vendorList() {
		List<User> users;
		ModelAndView mav=new ModelAndView();
		
		users = userRepository.getRegisteredUsers();
		if (users != null) {
			mav.addObject("users", users);
		}
		
		mav.setViewName("user/ListVendors");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView debtorsList() throws ParseException {
		List<User> users;
		List<String> debts;
		double debt;
		ModelAndView mav = new ModelAndView();
		
		users = userRepository.getDebtUsers();
		
		if (users != null) {
			mav.addObject("users", users);
			debts = new ArrayList<String>();
			
			for (User user : users) {
				debt = 0;
				List<Purchase> sales = purchaseRepository.getSoldItems(user);
				for (Purchase sale : sales) {
					if (sale.isDebtPending()) {
						debt += sale.calcCommission();
					}
				}
				
				NumberFormat twoDForm =  NumberFormat.getInstance(Locale.getDefault());
				twoDForm.setMinimumFractionDigits(2);
				twoDForm.setMaximumFractionDigits(2);
				String num = twoDForm.format(debt);
				debts.add(num);
			}
			mav.addObject("debts", debts);
		}
		
		mav.setViewName("/admin/ListDebtors");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView listUserCalification(@RequestParam("order") String order) {
		ModelAndView mav = new ModelAndView();
		if(order==null)
			order="positive";
		List<User> users = userRepository.getPointsFromAll(order);
		mav.addObject("users", users);
		mav.setViewName("/user/ListUserCalification");
		return mav;
	}
}
